#define APPLYROTATESANDSYMMETRIES
/*
 * Board.hpp
 *
//l:
//0            00
//1          01  02
//2        03  04  05
//3      06  07  08  09
//4    10  11  12  13  14
// c:00  01  02  03  04
 *
//Rotation 60 CW
//l:
//0            10
//1          11  06
//2        12  07  03
//3      13  08  04  01
//4    14  09  05  02  00
// c:00  01  02  03  04
*
//Rotation 120 CW
//l:
//0            14
//1          09  13
//2        05  08  12
//3      02  04  07  11
//4    00  01  03  06  10
// c:00  01  02  03  04
 *
 // 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14
 *
 *  Created on: 10 déc. 2011
 *      Author: Elca
 */

#ifndef BOARD_HPP_
#define BOARD_HPP_

#include "def.hpp"
#include "HolePosition.hpp"
#include "HoleJump.hpp"
#include "HolePermutation.hpp"
#include <iostream>
#include <vector>

using namespace std;

namespace pcm {

enum HolePermutationEnum{
	EReverse = 0,
	ECW60 = 1,
	ECCW60 =2,
	EReverseCW60 = 3,
	EReverseCCW60 =4
};

class BoardInfos {
public:

	//Construtor number
	static LL cons;

	BoardInfos();

	inline BoardInfos(const BoardInfos* _boardInfos){
		cons++;
		m_board = _boardInfos->m_board;
		m_numberJoin = _boardInfos->m_numberJoin;
		m_numberPeg = _boardInfos->m_numberPeg;
	}
	virtual ~BoardInfos();

	inline static int gameSize() {
		return m_gameSize;
	}
	inline static int gameNumberHole() {
		return m_gameNumberHole;
	}
	inline static const HolePosition& holePosition(short int hole) {
		return m_holePosition[hole];
	}
	inline static const vector<HoleJump>& startHoleJumps(short int hole) {
		return m_startHoleJumps[hole];
	}
	inline static const vector<HoleJump>& endlHoleJumps(short int hole) {
		return m_endHoleJumps[hole];
	}

	inline static bool hole(LL _boardToGet,short int _holeToGet) {
		return (_boardToGet>>_holeToGet)&1;
	}
	inline static bool hole(LL _boardToGet,short int _lignToGet,short int _colToGet){
		return hole(_boardToGet,HolePosition::convToHoleNumber(_lignToGet,_colToGet));
	}
	inline static void setHole(LL &_boardToSet,short int holeToSet, bool setPeg){
		LL mask = (LL)1<<holeToSet;
		if(setPeg){
			_boardToSet |=mask ;
		}else{
			_boardToSet &= ~mask;
		}
	}
	inline static void copyHole(LL &_boardToCopy,LL _baseBoard, short int _holeTo,short int _holeFrom){
		setHole(_boardToCopy,_holeTo,hole(_baseBoard,_holeFrom));
	}

	inline static void swap(LL &_boardToSwap, short int _hole1,short int _hole2){
		bool tmpHole1 = hole(_boardToSwap,_hole1);
		setHole(_boardToSwap,_hole1,hole(_boardToSwap,_hole2));
		setHole(_boardToSwap,_hole2,tmpHole1);
	}

	inline void setHole(short int holeToSet, bool setPeg){
		LL mask = (LL)1<<holeToSet;
		if(setPeg){
			m_board |=mask ;
		}else{
			m_board &= ~mask;
		}
	}

	inline bool hole(short int _holeToGet) const{
		return (m_board>>_holeToGet)&1;
	}
	inline bool hole(short int _lignToGet,short int _colToGet) const{
		return hole(HolePosition::convToHoleNumber(_lignToGet,_colToGet));
	}

	inline void swap(short int _hole1,short int _hole2){
		LL tmpHole1 = hole(m_board,_hole1);
		setHole(m_board,_hole2);
		setHole(m_board,tmpHole1);
	}

	inline LL* boardPointer(){
		return &m_board;
	}
	inline LL board() const{
		return m_board;
	}
	static inline LL reverseAllBoard(LL _board) {
		LL tmp = _board;
		for(unsigned short int i=0;i<LLBITSDIV2;i++){
			swap(tmp,i,LONLONGBITSM1-i);
		}
		return tmp;
	}
	inline short int numberPeg() const {
		return m_numberPeg;
	}
	inline void decrementNumberPeg(){
		m_numberPeg -=1;
	}
	inline LL numberJoin() const {
		return m_numberJoin;
	}
	inline void incrementNumberJoin(LL _increment=1){
		m_numberJoin+=_increment;
	}

	inline BoardInfos* applyMove(HoleJump move){
		BoardInfos* board = new BoardInfos(this);
		board->setHole(move.start(),false);
		board->setHole(move.jump(),false);
		board->setHole(move.end(),true);
		board->decrementNumberPeg();
#ifdef APPLYROTATESANDSYMMETRIES
		board->applyHolesPermutations();
#endif
		return board;
	}

	inline void applyHolesPermutations(){
		LL actualBoard= m_board;

		//CW60
		vector<HolePermutation>& CW60 = m_holesPermutation[ECW60];
		LL tmpCW60 = actualBoard;
		size_t size = m_holesPermutationSize[ECW60];
		for(size_t i=0;i<size;i++){
			copyHole(tmpCW60,actualBoard, CW60[i].start(),CW60[i].end());
		}

		//Check if rotation is same board
		if(tmpCW60!=m_board){

			if(tmpCW60<m_board){
				m_board = tmpCW60;
			}

			//Check reverse to see if is symetric
			vector<HolePermutation>& reverse = m_holesPermutation[EReverse];
			LL tmpReverse = actualBoard;
			size = m_holesPermutationSize[EReverse];
			for(size_t i=0;i<size;i++){
				swap(tmpReverse,reverse[i].start(),reverse[i].end());
			}

			if(actualBoard!=tmpReverse){

				if(tmpReverse<m_board){
					m_board = tmpReverse;
				}

				//Reverse CW60
				vector<HolePermutation>& reverseCW60 = m_holesPermutation[EReverseCW60];
				LL tmpReverseCW60 = actualBoard;
				size = m_holesPermutationSize[EReverseCW60];
				for(size_t i=0;i<size;i++){
					copyHole(tmpReverseCW60,actualBoard, reverseCW60[i].start(),reverseCW60[i].end());
				}
				if(tmpReverseCW60<m_board){
					m_board = tmpReverseCW60;
				}
				//Reverse CCW60
				vector<HolePermutation>& reverseCCW60 = m_holesPermutation[EReverseCCW60];
				LL tmpReverseCCW60 = actualBoard;
				size = m_holesPermutationSize[EReverseCCW60];
				for(size_t i=0;i<size;i++){
					copyHole(tmpReverseCCW60,actualBoard, reverseCCW60[i].start(),reverseCCW60[i].end());
				}
				if(tmpReverseCCW60<m_board){
					m_board = tmpReverseCCW60;
				}
			}

			//CCW60
			vector<HolePermutation>& CCW60 = m_holesPermutation[ECCW60];
			LL tmpCCW60 = actualBoard;
			size = m_holesPermutationSize[ECCW60];
			for(size_t i=0;i<size;i++){
				copyHole(tmpCCW60,actualBoard, CCW60[i].start(),CCW60[i].end());
			}
			if(tmpCCW60<m_board){
				m_board = tmpCCW60;
			}
		}
	}

	inline BoardInfos applyMoveStack(HoleJump move){
		BoardInfos board(this);
		board.setHole(move.start(),false);
		board.setHole(move.jump(),false);
		board.setHole(move.end(),true);
		board.decrementNumberPeg();
#ifdef APPLYROTATESANDSYMMETRIES
		board.applyHolesPermutations();
#endif
		return board;
	}

	static void printBoard(LL _board,short int _size, string _name="") ;
	static void updateGameInfos(short int gameSize);
	void initBoard(short int holeToRemove);
	void fillBoard();
	void calculateHolesPos();
	void calculateHolesJumps();
	void calculateRotatesAndSymmetries();
	void printBoard() const;
	void printJumps() const;
	void printHolePos() const;

protected:
	static short int m_gameNumberHole;
	static short int m_gameSize;
	static HolePosition *m_holePosition;
	static vector<HoleJump> *m_startHoleJumps;
	static vector<HoleJump> *m_endHoleJumps;
	static vector<HolePermutation> *m_holesPermutation;
	static size_t *m_holesPermutationSize;

	LL m_board;
	short int m_numberPeg;
	LL  m_numberJoin;

	inline static  void setGameNumberHole(short int gameSize){
		m_gameNumberHole = (gameSize+1)*(gameSize/2.0);
	}

	inline static void setGameSize(short int gameSize){
		BoardInfos::m_gameSize = gameSize;
	}
private:
};

} /* namespace pcm */
#endif /* BOARD_HPP_ */
